feat: support application version release documents [PYSDK-122]#612
feat: support application version release documents [PYSDK-122]#612arne-aignx wants to merge 16 commits intomainfrom
Conversation
Configures the project for the sdlc-* skills: JIRA project key (PYSDK), Atlassian Cloud ID, Ketryx project (Python SDK), per-item-type locations with Git preferred for SWRs and SPECs, and the approval structure pulled from Ketryx project settings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…PYSDK-122]
Regenerate the auto-generated Python client from the public OpenAPI spec
v1.5.0, which adds the four release-documents endpoints under
/api/v1/applications/{application-id}/versions/{version-id}/documents
(list, get metadata, /file, /content) plus the supporting
VersionDocumentResponse model and VersionDocumentVisibility enum.
This commit is intentionally isolated to keep the generated diff
reviewable; the platform-resource wrapper, CLI, and tests follow in
subsequent commits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…e documents [PYSDK-122]
Expose the four /api/v1/applications/{id}/versions/{version}/documents
endpoints (list, get metadata, /file, /content) added in OpenAPI v1.5.0
through a new Documents resource on the platform module.
* New Documents class with list(), details(), download_to_path(),
get_download_url(), and get_content_url() methods. The two redirect
endpoints (/file and /content) are resolved with allow_redirects=False
using the same pattern as Artifact.get_download_url() so the short-lived
presigned GCS URL can be inspected before the body is consumed.
* New ApplicationVersionDocument Pydantic wrapper around the codegen
VersionDocumentResponse model.
* Versions.documents(application_id, application_version) returns a bound
Documents instance.
* list() and details() are cached with the existing
application_version_cache_ttl (5 min); the redirect endpoints are
intentionally not cached because the URLs are short-lived.
* No CRC32C checksum verification on download — PAPI does not expose a
checksum for documents; integrity is bounded by HTTPS and signed-URL TTL.
* Documents and ApplicationVersionDocument re-exported from the platform
package.
Unit tests cover happy-path list/details/download/content-url, 404 →
NotFoundException, cache hit + nocache=True bypass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…commands [PYSDK-122]
Wire the new platform Documents resource into the application CLI so users
can discover and download an application version's public release documents
without writing Python.
* New `aignostics application version` Typer subgroup (mirrors `run`).
* Nested `aignostics application version document` subgroup with:
* `list APPLICATION_VERSION_ID` — tabulated metadata (id, name, mime
type, timestamps), supports `--format text|json`.
* `describe APPLICATION_VERSION_ID DOCUMENT_NAME` — full metadata for
one document, supports `--format text|json`. Maps NotFoundException
to exit code 2 with `Document '{name}' not found for application
version '{ver}'.`
* `download APPLICATION_VERSION_ID DOCUMENT_NAME [--output PATH]` —
streams the file to disk via the platform 307 redirect; default
destination is the current working directory using the document's
server-provided filename.
APPLICATION_VERSION_ID accepts either `application_id` (latest version is
resolved via Versions.latest) or `application_id:version_number` for an
explicit semantic version pin.
Unit tests cover the four BDD scenarios from TC-APPLICATION-CLI-05:
list, describe, describe-not-found, and download.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…en [PYSDK-122] * Bump Date / Version frontmatter on SPEC-APPLICATION-SERVICE and SPEC_PLATFORM_SERVICE to today's date. * Fix lint findings surfaced by `make lint` after the codegen + Documents resource changes (DOC201 / DOC501 / PLC1901 / PLR6104 / formatting + pyright reportCallIssue on cached_operation's injected `nocache` kwarg). * Add the new required `input_artifacts` field that the v1.5.0 ItemResultReadResponse model now demands to the test helper in tests/aignostics/application/utils_test.py — without it three utils_test cases fail Pydantic validation. `make lint`, `make test_unit`, and `make audit` all pass after this change. `make test_integration` reports one pre-existing GUI-pagination failure that hits the production API without a JWT and was already failing on the parent commit (no relation to this CR). The CHANGELOG.md is generated by git-cliff at release time; the per-CR entry will be produced from the conventional-commit messages in this branch when `make publish-release` runs, so no manual entry is added. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…t download Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds SDK + CLI support for application version release documents by updating the generated OpenAPI client to v1.5.0 and introducing a new Documents resource reachable via client.applications.versions.documents(application_id, version).
Changes:
- Regenerates
aignx.codegenfrom OpenAPI 1.5.0, including version document endpoints/models andinput_artifactsin item results. - Adds
Documents+ApplicationVersionDocumentto the platform resources, including redirect-resolution and download helpers. - Adds
aignostics application version documentCLI subgroup and updates specs/requirements + unit/feature tests.
Reviewed changes
Copilot reviewed 12 out of 59 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/aignostics/platform/resources/applications_test.py | Adds unit coverage for the new Documents resource (list/details/redirect/download + cache behavior). |
| tests/aignostics/application/utils_test.py | Updates test helper to populate input_artifacts to match updated codegen models. |
| tests/aignostics/application/cli_test.py | Adds CLI unit tests for application version document commands (list/describe/download + 404 handling). |
| tests/aignostics/application/TC-APPLICATION-CLI-05.feature | Introduces BDD test case text for version document CLI behavior. |
| src/aignostics/platform/resources/applications.py | Implements Versions.documents(), Documents resource, and ApplicationVersionDocument wrapper model. |
| src/aignostics/platform/init.py | Exposes Documents and ApplicationVersionDocument in the public platform API surface. |
| src/aignostics/application/_cli.py | Adds application version document CLI subgroup + parsing/resolution helpers. |
| specifications/SPEC_PLATFORM_SERVICE.md | Updates platform spec to include release document support + schema and API snippets. |
| specifications/SPEC-APPLICATION-SERVICE.md | Updates application spec to include CLI capabilities for release documents. |
| requirements/SWR-APPLICATION-1-3.md | Adds new functional requirement for listing/describing/downloading release documents. |
| codegen/out/docs/PublicApi.md | Documents the newly generated public endpoints for version documents. |
| codegen/out/aignx/codegen/rest.py | Bumps generated OpenAPI doc version marker to 1.5.0. |
| codegen/out/aignx/codegen/models/version_read_response.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/version_document_visibility.py | Adds new generated enum model for document visibility. |
| codegen/out/aignx/codegen/models/version_document_response.py | Adds new generated model for version document metadata. |
| codegen/out/aignx/codegen/models/validation_error_loc_inner.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/validation_error.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/user_read_response.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/scheduling_response.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/scheduling_request.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/run_termination_reason.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/run_state.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/run_read_response.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/run_output.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/run_item_statistics.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/run_creation_response.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/run_creation_request.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/output_artifact_visibility.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/output_artifact_scope.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/output_artifact_result_read_response.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/output_artifact.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/organization_read_response.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/me_read_response.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/item_termination_reason.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/item_state.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/item_result_read_response.py | Adds input_artifacts field + serialization/deserialization updates. |
| codegen/out/aignx/codegen/models/item_output.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/item_creation_request.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/input_artifact_result_read_response.py | Adds new generated model for input artifact results. |
| codegen/out/aignx/codegen/models/input_artifact_creation_request.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/input_artifact.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/http_validation_error.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/custom_metadata_update_response.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/custom_metadata_update_request.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/artifact_termination_reason.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/artifact_state.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/artifact_output.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/application_version.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/application_read_short_response.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/application_read_response.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/models/init.py | Exports newly generated models (VersionDocument*, InputArtifactResultReadResponse). |
| codegen/out/aignx/codegen/exceptions.py | Updates generated header/version marker. |
| codegen/out/aignx/codegen/configuration.py | Updates generated header/version marker + debug report API version string. |
| codegen/out/aignx/codegen/api_client.py | Updates generated header/version marker. |
| codegen/out/.openapi-generator/FILES | Updates generator file manifest to include new generated models. |
| codegen/in/openapi.json | Updates input OpenAPI spec to 1.5.0 and adds the new version document endpoints/schemas. |
| codegen/in/archive/openapi_1.5.0.json | Adds archived copy of the 1.5.0 OpenAPI spec. |
| CLAUDE.md | Adds SDLC configuration section (process/documentation metadata). |
…[PYSDK-122] Drop the get_content_url / get_download_url public methods on the Documents resource and the _resolve_redirect_url / _fetch_redirect_url machinery that intercepted the platform 307 to extract a signed URL — there were no in-SDK callers for the URL surface, and following the redirect with requests (allow_redirects=True) is sufficient for download_to_path. requests strips the Authorization header on the cross-host hop, so the bearer token is not forwarded to GCS. Also address Copilot review on PR #612: - URL-encode each path segment in download_to_path (application_id, version, document_name) so reserved characters in a document name cannot inject extra path segments. - Split application/version "is unavailable" 404s from document-level 404s in the describe / download CLI commands so error messages stop conflating the two failure modes. - Update SPEC_PLATFORM_SERVICE.md so the resource table and Documents class snippet match the implementation (constructor signature, list() return type, no get_content_url). - Reword TC-APPLICATION-CLI-05 scenario 04 to match actual behaviour: the document file is written using the requested document name, not a server-provided filename. Net change: -167 lines after redirect simplification; +13 / -25 across the remaining files for the encoding and CLI split. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
❌ 1 Tests Failed:
View the top 1 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
…K-122] Split Documents.download_to_path into three helpers (_resolve_destination_path, _build_document_endpoint_url, _stream_document) so cognitive complexity drops below the S3776 threshold. Lift recurring test literals into module-level constants where it improves clarity, and apply NOSONAR with rule + reason for literals where a constant would be less informative than the literal itself. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…YSDK-122]
Adds the in-memory read variant of release-document download backed by
GET .../documents/{name}/content. Unlike /file (which carries a
Content-Disposition: attachment header for browser downloads),
/content lets the GCS-stored Content-Type pass through and is intended
for programmatic consumers that want the raw bytes (small JSON
manifests, license text, etc.).
Refactors _build_document_endpoint_url to take a suffix parameter and
_stream_document to take a writer callable instead of opening the file
itself, so download_to_path and read_content share the request, retry,
and error-mapping path. Updates the Documents SPEC to advertise the new
method.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…SDK-122] Adds 15 unit tests for the document list/describe/download CLI commands covering the unresolvable-version path, the missing-document path, and generic-failure path in both text and JSON output formats. Reuses test literals via shared module-level constants to keep SonarCloud's S1192 duplicate-string check quiet. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…K-122] Replace non-standard `# noqa: ... -- justification` form with the documented ruff syntax plus a preceding rationale comment, and lift the duplicated "Not Found" / "a.pdf" string literals into module-level test constants so the duplicate-literal smell is resolved without NOSONAR suppressions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| - `version document list APPLICATION_VERSION_ID`: List public release documents attached to an application version | ||
| - `version document describe APPLICATION_VERSION_ID DOCUMENT_NAME`: Show metadata for a single public release document | ||
| - `version document download APPLICATION_VERSION_ID DOCUMENT_NAME [--output PATH]`: Download a public release document to a local path |
There was a problem hiding this comment.
| - `version document list APPLICATION_VERSION_ID`: List public release documents attached to an application version | |
| - `version document describe APPLICATION_VERSION_ID DOCUMENT_NAME`: Show metadata for a single public release document | |
| - `version document download APPLICATION_VERSION_ID DOCUMENT_NAME [--output PATH]`: Download a public release document to a local path | |
| - `version document list`: List public release documents attached to an application version | |
| - `version document describe`: Show metadata for a single public release document | |
| - `version document download`: Download a public release document to a local path |
We don't document the arguments to the other commands. I would suggest doing the same here for consistency.
There was a problem hiding this comment.
The style was a bit unclear to me.
- run list [--for-organization ORG_ID]: List application runs; supports listing all runs for an organization with --for-organization (only available to org admins)
documents the options.
Fine to remove for me.
There was a problem hiding this comment.
Oops.
Since we're already not consistent, I don't really mind either way. We can leave it as is.
|
|
||
| @document_app.command("list") | ||
| def application_version_document_list( | ||
| application_version_id: Annotated[str, typer.Argument(..., help=_APPLICATION_VERSION_ID_HELP)], |
There was a problem hiding this comment.
Other commands take the application ID and version as separate arguments (example). Is there a reason for doing it differently here, or should we do the same for consistency?
There was a problem hiding this comment.
Oh, I will change that. Was mislead by claude who was adamant, that this is the way to go in PySDK
| if version_number is None: | ||
| # Resolve the latest version for the application. | ||
| version = client.applications.versions.latest(application=application_id) | ||
| if version is None: | ||
| raise NotFoundException( | ||
| status=404, | ||
| reason=f"No versions found for application '{application_id}'.", | ||
| ) | ||
| version_number = version.number |
There was a problem hiding this comment.
This is pretty much the same as this, maybe we can do client.application_version(...)?
There was a problem hiding this comment.
I'll refactor. A bit unfamiliar with the code base here.
| Layer: System (backend logic) | ||
| --- | ||
|
|
||
| System shall list, describe, and download release documents attached to a given application version, exposing only documents with public visibility and uploaded status. |
There was a problem hiding this comment.
exposing only documents with public visibility and uploaded status.
This suggests the Python SDK pulls all documents, including internal docs, and only exposes public docs. In reality the API only exposes public docs, right? So Python SDK just exposes whatever it receives.
There was a problem hiding this comment.
What is PythonSDKs stance on surfacing fields?
IMO we could also get rid of the visibility all-together.
The API does expose it, but as you said correctly. PythonSDK will never see the internal documents.
There was a problem hiding this comment.
Yes, I would remove the notion of visibility: Python SDK shows the user whatever the platform exposes.
| Given the user has access to an application version with release documents attached | ||
| When the user requests the list of release documents for the application version | ||
| Then the system shall return metadata for documents with public visibility and uploaded status | ||
| And the system shall exclude documents with internal visibility or pending status |
There was a problem hiding this comment.
Similar to my comment above; AFAIU Python SDK does not exclude anything. Or am I missing something?
| @id:TC-APPLICATION-CLI-05-03 | ||
| Scenario: System rejects requests for non-existent or non-public release documents | ||
| Given the user has access to an application version | ||
| When the user requests metadata for a document that does not exist or is not public |
There was a problem hiding this comment.
If my understanding is correct, non-existent or non-public is the same for Python SDK.
7719458 to
a0028a7
Compare
a0028a7 to
4cac6f1
Compare
|



Summary
aignx.codegenfrom OpenAPI v1.5.0, which adds the four/api/v1/applications/{id}/versions/{ver}/documentsendpoints (list, getmetadata,
/fileredirect,/contentredirect) plus the supportingVersionDocumentResponsemodel andVersionDocumentVisibilityenum.Documentsresource onplatform(list,details,download_to_path,read_content)aignostics application version documentCLI subgroup withlist,describe, anddownloadcommands.SPEC-APPLICATION-SERVICE,SPEC_PLATFORM_SERVICE, andSWR-APPLICATION-1-3to reflect the new behaviour.Linked items
tests/aignostics/application/TC-APPLICATION-CLI-05.featureTest plan
make lintpasses (ruff + mypy + pyright)make test_unitpasses (new unit tests for Documents resource +CLI commands incl. happy-path, 404, cache hit,
nocache=True)make auditpassesaignostics application version document list <id>against staging
aignostics application version document download <id> <name>writes the file using the requested document name🤖 Generated with Claude Code